﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

using Odelle.Utils;
using Odelle.Utils.Comparisons;
using Odelle.WoWArmory.Data;

namespace Odelle.WoWArmory
{
    public delegate void CallbackMessenger(string message);

    public class CrawlArmory
    {
        private AccessArmory Access = new AccessArmory();

        public CrawlArmory()
        {
        }

        ~ CrawlArmory()
        {
            cbMessenger = null;
        }

        private CallbackMessenger cbMessenger;
        public void SetCallbackMessenger(CallbackMessenger delegateMessanger)
        {
            cbMessenger = delegateMessanger;
        }

        private void SendMessage(string message)
        {
            if (cbMessenger != null)
                cbMessenger(message);
        }
        
        public string[] GetRosterInGuild(string region, string realm, string guild)
        {
            List<string> roster = new List<string>();

            XmlQuery xquery = new XmlQuery();
            Access.SetRegion(region);

            int page =1;

            int total = 0;
            int subtotal = 0;

            while (true)
            {
               
                SendMessage("Fetching roster: " +  CharacterData.NamesToId(region , realm , guild) +" #" + page.ToString());
                string dhtml = Access.FetchArmoryGuild(realm, guild, page);

                if (dhtml != "")
                {
                    XmlDocument xmlHtml = new XmlDocument();
                    try
                    {
                        xmlHtml.LoadXml(dhtml);
                        subtotal = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\content-bot", @"class\results-end" }));
                        total = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\content-bot", @"class\results-total" }));
                    }
                    catch (Exception)
                    {
                        break;
                    }

                    if (subtotal > 0)
                    {
                        XmlElement baseNode = xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\content-bot", @"id\roster" });
                        foreach (XmlElement ele in baseNode.ChildNodes[0].ChildNodes[1].ChildNodes)
                        {
                            string name = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(ele,new string[] { @"class\name"}));
                            if (name != "")
                                roster.Add(name);
                        }
                    }
                }
                ++page;

                if (subtotal >= total)
                    break;
            }

            return roster.ToArray();
        }

        public CharacterData GetCharacterData(string ID)
        {
            string regionId = "";
            string realmId = "";
            string characterId = "";

            SendMessage("Fetching character: " + ID);

            CharacterData.IdToNames(ID, out regionId, out realmId, out characterId);
            Access.SetRegion(regionId);

            string dhtml = Access.FetchArmoryCharacter(realmId, characterId);
            if (dhtml == "")
            {
                return null;
            }

            XmlDocument xmlHtml = new XmlDocument();
            try
            {
                xmlHtml.LoadXml(dhtml);
            }
            catch (Exception)
            {
                return null;
            }

            XmlQuery xquery = new XmlQuery();

            string realm = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\realm*"}));
            string character = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\name"}));

            if (realm.ToLower() != realmId.ToLower() || character.ToLower() != characterId.ToLower())
                return null;

            CharacterData newData = new CharacterData();
            newData.Region = regionId.ToUpper();
            newData.Realm = realm;
            newData.Character = character;

            newData.Race = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\race" }));
            newData.ClassName = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\class*" }));

            string spec1 = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\summary-talents", @"data-spec-id\0", @"class\name-build" }));
            string spec2 = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\summary-talents", @"data-spec-id\1", @"class\name-build" }));
            newData.Spec1 = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\spec*" }));

            if (spec1 == "N/A")
                spec1 = "";
            if (spec2 == "N/A")
                spec2 = "";

            if (newData.Spec1 == spec1)
                newData.Spec2 = spec2;
            else
                newData.Spec2 = spec1;

            newData.Title = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\title" }));
            newData.Guild = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\guild" }));

            newData.Level = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\level" }));
            newData.Achievement = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"id\profile-wrapper", @"class\profile-info", @"class\achievements" }));

            string lastUpdate = DhtmlHandler.GetInnerText(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-lastupdate"}));
            string[] lup = lastUpdate.Split(' ');
            newData.Modified = lup[lup.Length - 1];
            DateTime d;
            if ( DateTime.TryParse(lup[lup.Length - 1], out d))
                newData.LastLogin = d;

            // main status
            newData.BaseHealth = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-health-resource", @"data-id\health", @"class\value" }));
            newData.BaseMana = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-health-resource", @"data-id\power-0", @"class\value" }));
            newData.BaseStrength = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-base", @"data-id\strength", @"class\value*" }));
            newData.BaseStamina = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-base", @"data-id\stamina", @"class\value*" }));
            newData.BaseAgility = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-base", @"data-id\agility", @"class\value*" }));
            newData.BaseIntellegence = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-base", @"data-id\intellect", @"class\value*" }));
            newData.BaseSpirit = DhtmlHandler.GetInnerInteger(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-base", @"data-id\spirit", @"class\value*" }));
            newData.BaseMastery = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-base", @"data-id\mastery", @"class\value*" }));

            newData.ItemLevelEquip = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-averageilvl", @"class\equipped*" }));
            newData.ItemLevel = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-averageilvl", @"class\best*" }));
            //

            newData.SpellPower = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\spellpower", @"class\value*" }));
            newData.SpellHaste = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\spellhaste", @"class\value*" }));
            newData.SpellHit = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\spellhit", @"class\value*" }));
            newData.SpellCrit = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\spellcrit", @"class\value*" }));
            newData.ManaRegenRest = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\manaregen", @"class\value*" }));
            newData.ManaRegenCombat = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\combatregen", @"class\value*" }));
 
            newData.MeleePower = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleeattackpower", @"class\value*" }));
            newData.MeleeDps = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleedps", @"class\value*" }));
            newData.MeleeDps2 = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleedps", @"class\value*" }),1);
            newData.MeleeSpeed = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleespeed", @"class\value*" }));
            newData.MeleeSpeed2 = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleespeed", @"class\value*" }),1);
            newData.MeleeHit = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleehit", @"class\value*" }));
            newData.MeleeCrit = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleecrit", @"class\value*" }));
            newData.MeleeHaste = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleehaste", @"class\value*" }));
            newData.MeleeHaste2 = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\meleehaste", @"class\value*" }),1);
            newData.Expertise = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\expertise", @"class\value*" }));
            newData.Expertise2 = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\expertise", @"class\value*" }),1);

            newData.RangePower = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\rangedattackpower", @"class\value*" }));
            newData.RangeDps = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\rangeddps", @"class\value*" }));
            newData.RangeSpeed = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\rangedspeed", @"class\value*" }));
            newData.RangeHit = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\rangedhit", @"class\value*" }));
            newData.RangeCrit = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\rangedcrit", @"class\value*" }));
            newData.RangeHaste = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\rangedhaste", @"class\value*" }));

            newData.DefenceArmor = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\armor", @"class\value*" }));
            newData.DefenceDodge = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\dodge", @"class\value*" }));
            newData.DefenceParry = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\parry", @"class\value*" }));
            newData.DefenceBlock = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\block", @"class\value*" }));
            newData.Resilience = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\resilience", @"class\value*" }));
            newData.PvpPower = DhtmlHandler.GetInnerDouble(xquery.QueryXmlSingleElement(xmlHtml.DocumentElement, new string[] { @"class\summary-stats-simple-other", @"data-id\pvppower", @"class\value*" }));

            // profession
            XmlElement[] elements;
            elements=            xquery.QueryXmlElements(new XmlElement[] { xmlHtml.DocumentElement }, new string[] { @"class\profile-progress*", @"class\profession-details", @"class\name" });
            if (elements.Length >= 2)
            {
                newData.Profession1 = DhtmlHandler.GetInnerText(elements[0]);
                newData.Profession2 = DhtmlHandler.GetInnerText(elements[1]);
            }
            elements = xquery.QueryXmlElements(new XmlElement[] { xmlHtml.DocumentElement }, new string[] { @"class\profile-progress*", @"class\profession-details", @"class\value" });
            if (elements.Length >= 2)
            {
                newData.Profession1Value = DhtmlHandler.GetInnerInteger(elements[0]);
                newData.Profession2Value = DhtmlHandler.GetInnerInteger(elements[1]);
            }

            newData.Version = "5.0";
            newData.LastUpdated = DateTime.Now;

            return newData;
        }
    }
}
